/*
 * miscdev.c
 *
 *  Created on: May 19, 2025
 *      Author: Li Jiahui<2199250859@qq.com>
 */

#include "miscdev.h"

/* GPIO Relay */

gpio_t relays[RelayMax] =
{
		{"Relay1", GPIOD, GPIO_PIN_2, OFF},
}; /* 定义了一个继电器数组 */

int init_relay(void)
{
	int	which;

	/* Turn all relays off */
	for(which=0; which<RelayMax; which++)
	{
		HAL_GPIO_WritePin(relays[which].group, relays[which].pin, GPIO_PIN_RESET);
	}

	return 0;
}

/* 控制指定继电器的开关状态 which即继电器开关状态 */
void turn_relay(int which, int status)
{
	GPIO_PinState	level;

	if(which >= RelayMax)
		return ;

	level = status==OFF ? GPIO_PIN_RESET : GPIO_PIN_SET; /* 继电器设置为高电平驱动 */

	HAL_GPIO_WritePin(relays[which].group, relays[which].pin, level);

	relays[which].status = status; /* 同步更新软件中记录的状态 */
}

/* get which relay current status */
int status_relay(int which)
{
	if(which >= RelayMax)
		return 0;

	return relays[which].status;
}


/* GPIO Led */
gpio_t	leds[LedMax] =
{
		{"RedLed", GPIOC, GPIO_PIN_9, OFF},
		{"GreenLed", GPIOC, GPIO_PIN_6, OFF},
		{"BlueLed", GPIOB, GPIO_PIN_2, OFF},
};

int init_led(void)
{
	int	which;

	/* Turn all LEDs off */
	for(which=0; which<LedMax; which++)
	{
		HAL_GPIO_WritePin(leds[which].group, leds[which].pin, GPIO_PIN_SET);
	}

	return 0;
}

/* 控制该led灯 ON/OFF */
void turn_led(int which, int status)
{
	GPIO_PinState	level;

	if(which >= LedMax)
		return ;

	level = status==OFF ? GPIO_PIN_SET : GPIO_PIN_RESET; /* LED高电平熄灭 */

	HAL_GPIO_WritePin(leds[which].group, leds[which].pin, level);

	leds[which].status = status;
}

/* Led 状态切换 */
void toggle_led(int which)
{
	if(which >= LedMax)
		return ;

	HAL_GPIO_TogglePin(leds[which].group, leds[which].pin); /* 库函数自动翻转GPIO引脚电平 */

	leds[which].status = !leds[which].status;
}

/* Blink which led */
void blink_led(int which, uint32_t interval)
{
	turn_led(which, ON);
	HAL_Delay(interval);

	turn_led(which, OFF);
	HAL_Delay(interval);
}

/* get which led current status */
int status_led(int which)
{
	if(which >= LedMax)
		return 0;

	return leds[which].status;
}


#include "main.h"
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	static uint8_t relay1_status = OFF;

	if(Key1_Pin == GPIO_Pin)
	{
		relay1_status ^= 1;
		turn_relay(Relay1, relay1_status);
		toggle_led(Led_B);
	}
}


/* printf */
#include "usart.h" /* 全局变量huart1定义在 usart.c 中，并在usart.h头文件中声明*/

/*gcc编译器中的printf函数会调用__io_putchar()函数，实现最终的字符串打印
 * keil编译器中的printf函数会调用fputc（）函数，实现最终的字符串打印
 * 这里定义一个宏PUTCHAR_PROTOTYPE来兼容这两个编译器所需要的函数原型
 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

/*STM32CubeIDE使用的是gcc编译器，他会内建__GUNC__宏定义，所以下面的PUTCHAR_PROTOTYPE将会扩展为：
 * int __io_putchar(int ch)
 * */
PUTCHAR_PROTOTYPE
{
	/* 调用STM32 HAL库的串口发送函数，将printf要打印的这个字符通过串口发送出去 */
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}


/* ADC noisy & sensor API */

#include "adc.h"

/* 通过ADC采样获取光照和噪声传感器的原始数值 */
int adc_sample_lux_noisy(uint32_t *lux, uint32_t *noisy)
{
	uint8_t		i;
	uint32_t	timeout = 0xffffff; /* ADC转换超时阈值，防止卡死在等待状态 */

	for(i=0; i<ADCCHN_MAX; i++) /* 遍历所有ADC通道 */
	{
		HAL_ADC_Start(&hadc1); /* 启动ADC1的转换 */

		HAL_ADC_PollForConversion(&hadc1, timeout); /* 阻塞等到ADC转换完成，超时则退出 */

		/* 仅保存噪声和光照通道的值 */
		if(ADCCHN_NOISY == i)
		{
			*noisy = HAL_ADC_GetValue(&hadc1);
		}
		else if(ADDCHN_LUX == i)
		{
			*lux = HAL_ADC_GetValue(&hadc1);
		}

		HAL_Delay(10);
	}

	HAL_ADC_Stop(&hadc1);

	return 0;
}

/* Timer delay API */
#include "tim.h"

/* MAx to 60000 us */
void udelay(uint16_t us)
{
	uint16_t	differ = 60000 - us;

	HAL_TIM_Base_Start(&htim6);

	__HAL_TIM_SET_COUNTER(&htim6, differ); /* 将计算好的初值写入TMIM6计数器寄存器 */

	while(differ < 60000)
	{
		differ = __HAL_TIM_GET_COUNTER(&htim6); /* 读取当前计数器的值 */
	}

	HAL_TIM_Base_Stop(&htim6);
}

/* Bazzer API */

/* times:蜂鸣器响几次， interval：响、停的时间间隔 */
void beep_start(uint16_t times, uint16_t interval)
{
	while(times--)
	{
		/* start buzzer */
		if(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4) != HAL_OK)
		{
			/* start error */
			Error_Handler();
		}

		HAL_Delay(interval);

		/* stop buzzer */
		if(HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_4) != HAL_OK)
		{
			Error_Handler();
		}

		HAL_Delay(interval);
	}
}